<?php
$dir = dirname(__FILE__);
require_once "{$dir}/utils/llsd.php";
require_once "{$dir}/utils/xmlrpc.php";
require_once "{$dir}/types.php";
require_once "{$dir}/assets.php";
require_once "{$dir}/worldmap.php";
unset($dir);

class simcaps
{

	// Constants
	const DEFAULT_GRID = 'agni';
	
	// Private variables
	private $username;
	private $lastname;
	private $password;
	private $useragent;
	private $capseed;
	private $capabilities;
	private $slversion;
	private $inventory;
	private $debug = 1;
	private $seedfile = '';
	
	private $curl;
	
	private function _postdata($url, $data, $raw = false)
	{
		if(!is_resource($this->curl))
		{
			return false;
		}
		curl_setopt($this->curl, CURLOPT_POST, true);
		curl_setopt($this->curl, CURLOPT_HTTPHEADER,$raw?array():array("Content-type: text/xml"));
		curl_setopt($this->curl, CURLOPT_URL, $url);
		curl_setopt($this->curl, CURLOPT_POSTFIELDS, $data);
		return curl_exec($this->curl);
	}
	
	private function _getdata($url)
	{
		if(!is_resource($this->curl))
		{
			return false;
		}
		curl_setopt($this->curl, CURLOPT_POST, false);
		curl_setopt($this->curl, CURLOPT_HTTPHEADER,array());
		curl_setopt($this->curl, CURLOPT_URL, $url);
		curl_setopt($this->curl, CURLOPT_POSTFIELDS,'');
		return curl_exec($this->curl);
	}
	
	public function _debug($text, $level)
	{
		if($this->debug >= $level)
		{
			print "{$text}\n";
		}
	}
	
	private function _getfolders_recursive($root, $invarray)
	{
		// Implement later.
	}
	
	public function __construct($username = NULL, $lastname = NULL, $password = NULL, $useragent = '')
	{
		if(NULL !== $password)
		{
			$this->username = $username;
			$this->lastname = $lastname;
			$this->password = $password;
		}
		$this->useragent = ($useragent=='')?"phpsimcaps":"{$useragent} (using phpsimcaps)";
		$this->capseed = '';
		$this->capabilities = array();
		$this->curl = curl_init();
		curl_setopt($this->curl, CURLOPT_CONNECTTIMEOUT,120);
		curl_setopt($this->curl, CURLOPT_TIMEOUT,120);
		curl_setopt($this->curl, CURLOPT_RETURNTRANSFER,true);
		curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER,false);
		curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST,false);
	}
	
	public function __destruct()
	{
		curl_close($this->curl);
	}
	
	public function setdebug($level)
	{
		$this->debug = (int)$level;
	}
	
	public function enableseed($filename = 'seed.dat')
	{
		$this->seedfile = $filename;
	}
	
	public function getfolderid($folder)
	{
		if('/' == $folder)
		{
			return $this->inventory['id'];
		}
		$parts = explode('/',$folder);
		array_shift($parts);
		$curinvent = $this->inventory;
		if(isset($curinvent['subdirectories'][$parts[0]]))
		{
			return $curinvent['subdirectories'][$parts[0]]['folder_id'];
		}
		else
		{
			return new LLUUID();
		}
	}
	
	public function getversion($real = false)
	{
//		if(!$real)
		if(false)
		{		
			//HACK FOR EFFICIENCY: Using version 1.50.50.50 will always allow you to login
			$this->slversion = array(
				'major' => 1,
				'minor' => 50,
				'patch' => 50,
				'build' => 50
			); 
		}
		if(is_array($this->slversion) && (!$real || $this->slversion != array('major' => 1,'minor' => 50,'patch' => 50,'build' => 50)))
		{
			return $this->slversion;
		}
		preg_match('~([0-9]+?)\.([0-9]+?)\.([0-9]+?)\.([0-9]+?).+?\([0-9]+?MB\)~',$this->_getdata("http://secondlife.com/community/downloads.php"),$matches);
		
		$this->slversion = array(
			'major' => $matches[1],
			'minor' => $matches[2],
			'patch' => $matches[3],
			'build' => $matches[4]
		);
		
		return $this->slversion;
	}
	
	public function loggedin()
	{
		return ('' != $this->capseed);
	}
	
	public function hascapability($cap)
	{
		return isset($this->capabilities[$cap]);
	}
	
	public function login($grid = self::DEFAULT_GRID)
	{
		$version = $this->getversion();
		// TODO: This could be neater.
		$request = array(
			'first' => $this->username,
			'last' => $this->lastname,
			'passwd' => '$1$'.md5($this->password),
			'start' => 'last',
			'major' => $version['major'],
			'minor' => $version['minor'],
			'patch' => $version['patch'],
			'build' => $version['build'],
			'platform' => 'Mac',
			'mac' => '00:00:00:00:00:00',
			'options' => array(
				'inventory-root',
				'inventory-skeleton'
			),
			'user-agent' => $this->useragent
		);
		$xmlrpc = new SimpleXMLRPCClient();
		$xmlrpc->debug = ($this->debug > 5);
		$xmlrpc->SetXMLRPCServer("login.{$grid}.lindenlab.com","/cgi-bin/login.cgi",NULL,true);
		$xmlrpc->CreateXMLRPCMessage('login_to_simulator');
		$xmlrpc->AddArray($request);
		$logindata = $xmlrpc->SendXMLRPC();
		$this->_debug(print_r($logindata,true),5);
		// Work out the inventory structure
		$array = $logindata[0];
		if(!isset($array['seed_capability']))
		{
			return false;
		}
		$this->capseed = $array['seed_capability'];
		$this->inventory = array(
			'name' => 'My Inventory',
			'id' => new LLUUID($array['inventory-root'][0]['folder_id']),
			'subdirectories' => array()
		); 
		foreach($array['inventory-skeleton'] as $folder)
		{
			if($this->inventory['id']->tostring() == $folder['parent_id'])
			{
				$this->inventory['subdirectories'][$folder['name']] = array(
					'folder_id' => new LLUUID($folder['folder_id']),
					'type' => $folder['type_default'],
					'name' => $folder['name']
				);
			}
		}		
		$this->_debug("Inventory:\n".print_r($this->inventory,true),5);
		$request = array(
			'MapLayer',
			'MapLayerGod',
			'NewFileAgentInventory',
			'EventQueueGet',
			'RequestTextureDownload',
			'SendPostcard',
			'SendUserReport',
			'SendUserReportWithScreenshot',
			'UpdateGestureAgentInventory',
			'UpdateGestureTaskInventory',
			'UpdateNotecardAgentInventory',
			'UpdateNotecardTaskInventory'
		);
		if(false === ($response = $this->sendrequest($this->capseed,$request)))
		{
			return false;
		}
		$this->capabilities = $response;
		$this->_debug("CapsSeed array:\n".print_r($this->capabilities,true),5);
		
		return true; //I guess we got what we wanted.
	}

	public function sendrequest($capability, $data)
	{
		$capsurl = '';
		if(strpos($capability,'https://') === 0)
		{
			$capsurl = $capability;
		}
		else
		{
			if(isset($this->capabilities[$capability]))
			{
				$capsurl = $this->capabilities[$capability];
			}
			else
			{
				return false;
			}
		}
		$encoded = llsd_encode($data);
		$this->_debug($encoded,6);
		$decoded = llsd_decode($this->_postdata($capsurl,$encoded));
		$this->_debug(print_r($decoded,true),6);
		return $decoded;
	}
	
	public function sendrawdata($url, $data)
	{
		return llsd_decode($this->_postdata($url,$data,true));
	}
	
}
?>
